package com.zjq.springsocial.renren.api.impl;

import java.net.URI;
import java.util.List;

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig.Feature;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.social.NotAuthorizedException;
import org.springframework.social.oauth2.AbstractOAuth2ApiBinding;
import org.springframework.social.oauth2.OAuth2Version;
import org.springframework.social.support.ClientHttpRequestFactorySelector;
import org.springframework.social.support.URIBuilder;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import com.zjq.springsocial.renren.api.Image.ImageType;
import com.zjq.springsocial.renren.api.Renren;
import com.zjq.springsocial.renren.api.UserOperations;
import com.zjq.springsocial.renren.api.impl.json.RenrenModule;

/**
 * 
 * <p>This is the central class for interacting with Renren.</p>
 * <p>
 * There are some operations, such as searching, that do not require OAuth
 * authentication. In those cases, you may use a {@link RenrenTemplate} that is
 * created through the default constructor and without any OAuth details.
 * Attempts to perform secured operations through such an instance, however,
 * will result in {@link NotAuthorizedException} being thrown.
 * </p> 
 *
 * @author zhongjingquan
 *
 * @version 0.0.1-SNAPSHOT 2013-7-18上午10:24:49
 */
public class RenrenTemplate extends AbstractOAuth2ApiBinding implements Renren{

	private UserOperations userOperations;
	
	private ObjectMapper objectMapper;
	
	
	/**
	 * Create a new instance of RenrenTemplate.
	 * This constructor creates a new RenrenTemplate able to perform unauthenticated operations against Renren's Graph API.
	 * Some operations do not require OAuth authentication. 
	 * Those operations requiring authentication will throw {@link NotAuthorizedException}.
	 */
	public RenrenTemplate(){
		initialize();
	}
	
	public RenrenTemplate(String accessToken){
		super(accessToken);
		initialize();
	}
	
	// private helpers
	private void initialize() {
		super.setRequestFactory(ClientHttpRequestFactorySelector.bufferRequests(getRestTemplate().getRequestFactory()));
		initSubApis();
	}
	
	private void initSubApis() {
		userOperations = new UserTemplate(this,getRestTemplate(),isAuthorized());
	}
	
	// AbstractOAuth2ApiBinding hooks
	@Override
	protected OAuth2Version getOAuth2Version() {
		return OAuth2Version.BEARER;
	}
	
	@Override
	protected void configureRestTemplate(RestTemplate restTemplate){
		restTemplate.setErrorHandler(new RenrenErrorHandler());
	}
	
	@Override
	protected MappingJacksonHttpMessageConverter getJsonMessageConverter() {
		MappingJacksonHttpMessageConverter converter = super.getJsonMessageConverter();
		objectMapper = new ObjectMapper();
		objectMapper.registerModule(new RenrenModule());
		objectMapper.configure(Feature.WRITE_ENUMS_USING_INDEX, true);
		converter.setObjectMapper(objectMapper);
		return converter;
	}
	
	@Override
	public <T> T fetchObject(String objectId, Class<T> type) {
		URI uri = URIBuilder.fromUri(GRAPH_API_URL + objectId).build();
		return getRestTemplate().getForObject(uri, type);
	}

	@Override
	public <T> T fetchObject(String objectId, Class<T> type, MultiValueMap<String, String> queryParameters) {
		return null;
	}

	@Override
	public <T> List<T> fetchConnections(String objectId, String connectionName, Class<T> type, String... fields) {
		return null;
	}

	@Override
	public <T> List<T> fetchConnections(String objectId, String connectionName, Class<T> type, MultiValueMap<String, String> queryParameters) {
		return null;
	}

	@Override
	public byte[] fetchImage(String objectId, String connectionName, ImageType imageType) {
		return null;
	}

	@Override
	public String publish(String objectId, String connectionName, MultiValueMap<String, Object> data) {
		return null;
	}

	@Override
	public void post(String objectId, String connectionName, MultiValueMap<String, String> data) {
	}

	@Override
	public void delete(String objectId) {
	}

	@Override
	public void delete(String objectId, String connectionName) {
	}

	@Override
	public UserOperations getUserOperations() {
		return userOperations;
	}
}
